import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:bot_toast/bot_toast.dart';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:flustars/flustars.dart';

const bool inProduction = const bool.fromEnvironment("dart.vm.product");
enum Method { get, post, put, patch, delete, head }

class ResponseResultModel {
  var data;
  bool success;
  var headers;

  ResponseResultModel(this.data, this.success, {this.headers});
}

class ExceptionHandle {
  static const int success = 200;
  static const int success_not_content = 204;
  static const int unauthorized = 401;
  static const int forbidden = 403;
  static const int not_found = 404;
  static const int status_error = 503;

  static const int net_error = 1000;
  static const int parse_error = 1001;
  static const int socket_error = 1002;
  static const int http_error = 1003;
  static const int timeout_error = 1004;
  static const int cancel_error = 1005;
  static const int unknown_error = 9999;

  static Error handleException(dynamic error) {
    print(error);
    if (error is DioError) {
      if (error.type == DioErrorType.DEFAULT ||
          error.type == DioErrorType.RESPONSE) {
        dynamic e = error.error;
        if (e is SocketException) {
          return Error(socket_error, "网络异常，请检查你的网络！");
        }
        if (e is HttpException) {
          return Error(http_error, "服务器异常！");
        }
        if (error.type is DioErrorType) {
          return Error(status_error, error.message);
        }
        return Error(net_error, "网络异常，请检查你的网络！");
      } else if (error.type == DioErrorType.CONNECT_TIMEOUT ||
          error.type == DioErrorType.SEND_TIMEOUT ||
          error.type == DioErrorType.RECEIVE_TIMEOUT) {
        return Error(timeout_error, "连接超时！");
      } else if (error.type == DioErrorType.CANCEL) {
        return Error(cancel_error, "取消请求");
      } else {
        return Error(unknown_error, "未知异常");
      }
    } else {
      return Error(unknown_error, "未知异常");
    }
  }
}

class Error {
  int code;
  String msg;

  Error(this.code, this.msg);
}

class DioUtil {
  // 工厂模式
  factory DioUtil() => _getInstance();

  static DioUtil get instance => _getInstance();
  static DioUtil _instance;

  static DioUtil _getInstance() {
    if (_instance == null) {
      _instance = new DioUtil._internal();
    }
    return _instance;
  }

  Dio _dio;

  DioUtil._internal() {
    // 初始化

    _dio = new Dio();
    // 添加拦截器
    if (inProduction == false) {
//      dio.interceptors.add(LogInterceptor(responseBody: true));
      _dio.interceptors
          .add(InterceptorsWrapper(onRequest: (RequestOptions options) {
        StringBuffer log = StringBuffer();
        log.writeln(
            "\n================== 请求数据 ==========================${DateTime.now()}");
        log.writeln("url = ${options.uri.toString()}");
        log.writeln("headers = ${options.headers}");
        log.writeln("params = ${options.queryParameters}");
        log.writeln("method = ${options.method}");
        if (null != options.data) {
          log.writeln("data = ${jsonDecode(options.data)}");
        }
        LogUtil.v(log);
      }, onResponse: (Response response) {
        StringBuffer log = StringBuffer();
        log.writeln(
            "\n================== 响应数据 ==========================${DateTime.now()}");
        log.writeln("code = ${response.statusCode}");
        log.writeln("realUri = ${response.request.uri}");
        log.writeln("data = ${response.data}");
        LogUtil.v(log);
      }, onError: (DioError e) {
        StringBuffer log = StringBuffer();
        log.writeln("\n================== 错误响应数据 ======================");
        // log.writeln("realUri = $path");
        log.writeln("type = ${e.type}");
        log.writeln("message = ${e.message}");
        log.writeln("error = ${e.request.toString()}");
        log.writeln("response = ${e.response}");
        LogUtil.e(log);
      }));
    }

    Response response;
    try {
      _dio.options.baseUrl = '';
      _dio.options.connectTimeout = 15000;
      // dio.options.method = getRequestMethod(method);
      // dio.options.queryParameters =
      // Method.post == method ? FormData.fromMap(params) : params;
//      dio.options.contentType = ContentType.parse("application/json;charset=UTF-8");
      _dio.options.responseType = ResponseType.plain;
      var defaultHttpClientAdapter = DefaultHttpClientAdapter();
      defaultHttpClientAdapter.onHttpClientCreate = (HttpClient client) {
        client.badCertificateCallback =
            (X509Certificate cert, String host, int port) {
          return true;
        };
      };
      // dio.httpClientAdapter = defaultHttpClientAdapter;
      // if (header != null) {
      //   dio.options.headers.addAll(header);
      // }

      //await关键字声明运算为延迟执行，在Dart中，有await标记的运算，其结果值都是一个Future对象
      // response = await dio.request(path, data: data, cancelToken: cancelToken);
      // //返回正确的状态码
      // if (response.statusCode == ExceptionHandle.success ||
      //     response.statusCode == 201) {
      //   return new ResponseResultModel(
      //     response.data,
      //     true,
      //   );
      // } else {
      //   return new ResponseResultModel(response.data, false,
      //       headers: response.headers);
      // }
    } on DioError catch (e) {
      Error error = ExceptionHandle.handleException(e);
      // 返回错误信息
      // return new ResponseResultModel(
      //   error,
      //   false,
      // );
    }
  }

  Future<Response<T>> getSync<T>(String path,
      {Map<String, dynamic> params, CancelToken cancelToken}) async {
    return await _dio.get<T>(
      path,
      queryParameters: params,
      cancelToken: cancelToken,
    );
  }

  Future<Response<T>> postSync<T>(String path,
      {Map<String, dynamic> params,
      Map<String, dynamic> data,
      CancelToken cancelToken}) async {
    return await _dio.post<T>(
      path,
      queryParameters: params,
      data: FormData.fromMap(data),
      cancelToken: cancelToken,
    );
  }

  get<T>(String path, Function(T) onSuccess,
      Function(int code, String message) onError,
      {Map<String, dynamic> params, CancelToken cancelToken}) {
    var showLoading = BotToast.showLoading();
    _dio
        .get<T>(
      path,
      queryParameters: params,
      cancelToken: cancelToken,
    )
        .then((value) {
      showLoading();
      if (HttpStatus.ok == value.statusCode) {
        onSuccess(value.data);
      } else {
        onError(value.statusCode, value.statusMessage);
      }
    }).onError((error, stackTrace) {
      showLoading();
      onError(error.hashCode, error.toString());
      return null;
    });
  }

  post<T>(String path, Function(T) onSuccess,
      Function(int code, String message) onError,
      {Map<String, dynamic> params,
      Map<String, dynamic> data,
      CancelToken cancelToken}) {
    _dio
        .post<T>(
      path,
      queryParameters: params,
      data: FormData.fromMap(data),
      cancelToken: cancelToken,
    )
        .then((value) {
      if (HttpStatus.ok == value.statusCode) {
        onSuccess(value.data);
      } else {
        onError(value.statusCode, value.statusMessage);
      }
    });
  }

  static Future<ResponseResultModel> requestBase(
    String baseUrl,
    String path,
    Map<String, dynamic> params,
    Map<String, dynamic> header, {
    Method method = Method.get,
    noTip = false,
    data,
    CancelToken cancelToken,
  }) async {
//    var connectivityResult = await (new Connectivity().checkConnectivity());
//    if(connectivityResult == ConnectivityResult.mobile){
//
//    }else if(connectivityResult == ConnectivityResult.wifi) {
//
//    }
//    else if(connectivityResult == ConnectivityResult.none){
//
//      Error error = ExceptionHandle.handleException(DioError(type: DioErrorType.DEFAULT,error: SocketException("")));
//      // 返回错误信息
//      return new ResponseResultModel(error, false,);
//    }

    //处理请求头
    Map<String, dynamic> headers = new Map();
    if (header != null) {
      headers.addAll(header);
    }
  }

  static String getRequestMethod(Method method) {
    String m;
    switch (method) {
      case Method.get:
        m = "GET";
        break;
      case Method.post:
        m = "POST";
        break;
      case Method.put:
        m = "PUT";
        break;
      case Method.patch:
        m = "PATCH";
        break;
      case Method.delete:
        m = "DELETE";
        break;
      case Method.head:
        m = "HEAD";
        break;
    }
    return m;
  }
}

//通常异步函数返回的对象就是一个 Future
Future download(Dio dio, String url, savePath) async {
  try {
    await dio.download(
      url,
      savePath,
      onReceiveProgress: showDownloadProgress,
    );
  } catch (e) {
    print(e);
  }
}

void showDownloadProgress(received, total) {
  if (total != -1) {
    //toStringAsFixed: double -> String 注意括号中要有小数点位数，否则报错
    print((received / total * 100).toStringAsFixed(0) + "%");
  }
}
